リスコフの置換原則 (LSP)
Liskov Substitution Principle, LSP
基底型(親)から派生型(子)に置換可能であるときにのみ、継承関係を与えるべき、というもの
「Interfaceの正しい使い方」で直感的にイメージするものの話をしているmrsekut.icon
継承関係のあるものに応用できる
Interfaceの利用
ざっくりいうと
https://gyazo.com/b5e1cc1455012002473a0e6ee2768fd0
①ある箇所で、親を利用している時に、
②その親を、子に変えても全く正常に機能する場合にのみ、
③親と子は(この順序)で親子関係にできる
「親を子に置換可能であるべき」という原則
この図は、最初から親子と命名しているので、若干語弊があるが、言いたいことはわかるはずmrsekut.icon
classでなくInterfaceでも同じ
https://gyazo.com/1057c7ab9e7f2b5f2103ce283aba9bc9
LicenceというInterfaceと、それを実装している2つの~~Licenceclassがある
Billingclassはそれらの利用者
Interfaceを用いている場合は、そもそも①のような書き方をしているので
②の置換が可能になる
図の矢印は依存の向きなので、置換の向きではないことに注意mrsekut.icon
この意味で、継承もInterfaceも「リスコフの置換原則を満たしているもの」として同じものと捉えることができる
継承の解説時に「継承はInterfaceだ」のように言われるのは、恐らくこの原則のことを暗に前提している
以下の2つの実装者に対しての注意喚起と言える
継承を作ろうとしている人
今から継承関係のあるclassを作ろうとしている人
今から定義しようとしている、その子classは、親から置換可能ですか?
親のinterfaceのどれを使っても、子は正しく機能しますか?
抽象が存在するものを利用しようとしている人
上の図の利用者目線
今から使用しようとしているモノは、具象ではなく抽象に依存していますか?
要はこういうように書け、というもの
code:ts
const pl: Licence = new PersonalLicence(); // o
const pl: PersonalLicence = new PersonalLicence(); // x
PersonalLicenceを使う時に、PersonalLicenceの具体的な中身を意識して使うのではなく、LicenceのInterfaceを意識して使えよ、という感じ
継承関係として定義すべきでないものの例
defaultのOOP言語機能として提供される「継承」の作るための制約が弱すぎる
だから何でもかんでも継承にすることができるし、
実際それは原則を守っていないので、ゆくゆく破滅するということが生じる
言語機能が貧弱だし、人間も貧弱なので、契約プログラミングしてどうにかしよう、になる
リスコフの置換原則を無視して継承を作った時にどういう問題が生じるのか
なぜそもそもそういう原則が存在するのか?
関連